
/************************************************************************************************
*   深圳市摩西尔电子有限公司 @版本所有@
*
*   此文件用于硬件拓扑图接口
*
* 修改:
*   1. 类型 : 创建
*      作者 : 巫昭雯
*      时间 : 2020.12.30
*      内容 : 所有代码
*************************************************************************************************/


/* exported mc_topo_format_data */
/* exported mc_topo_foramt_card_idx_data */
/* exported mc_get_multi_recvcard_connect */
/* exported mc_constructors_hw_card_data */
/* exported init_svg_size */
/* exported mc_topo_create_block */
/* exported mc_zoom_mousewheel_callback */
/* exported init_event */
/* exported mc_get_send_card_req_img_array */
/* exported mc_construct_card_data_from_hw */
/* exported mc_callback_sand_card_connect */
/* exported mc_hw_swich_layout */


/* global $ */
/* global mc_sdk_param */
/* global mc_util_is_object */
/* global mc_util_is_array */
/* global mc_get_recvcard_msg */
/* global g_id_canvas_container */
/* global g_id_canvas_topo */
/* global g_id_canvas_line */
/* global mc_construct_line */
/* global mc_replace_line_key_letters */
/* global g_obj_instance_card_data */
/* global mc_fm_img_rece */
/* global g_id_layer_scale */
/* global mc_sendcard_outport_img_rect */
/* global mc_set_ary_img_data */
/* global mc_get_hubcard_connect */
/* global mc_get_recvcard_connect */
/* global mc_get_hubcard_outport_img_rect */
/* global mc_format_style */
/* global g_obj_lang */


// 全局工具
var g_util_foramt_data = new mc_topo_format_data();


/************************************************************************************************
 * 类型:
 *    构造函数
 * 功能:
 *    读写获取到的硬件数据
 * 参数:
 *    NA
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-08
 *       内容 : 所有代码
************************************************************************************************/
var mc_constructors_hw_card_data = function () {
    // 存储卡数据
    var m_arr_card_data = [];

    // 清空数据
    this.clear_data = function () {
        m_arr_card_data = [];
    };

    // 获取数据
    this.get_data = function () {
        return m_arr_card_data;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    转化读写参数值; 转化为 Boolean
     * 参数:
     *    @param { Promise<String> } str_val 读写参数字符串
     * 返回:
     *    @returns { Promise<Boolean> } 返回转换后的读写值 true === 读取 || false = 设置 || null === 参数错误
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-08
     *       内容 : 所有代码
    ************************************************************************************************/
    function trans_io_val(str_val) {
        if ("string" !== typeof str_val) {
            return null;
        }

        switch (str_val.trim()) {
        case "get":
            return true;
        case "set":
            return false;
        default:
            return null;
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取key字串h
     * 参数:
     *    @param { Promise<Number> } ui_send_i 发送卡下标
     *    @param { Promise<Number> } ui_send_port  发送卡输出口下标
     *    @param { Promise<Number> } ui_hub_i hub卡下标
     *    @param { Promise<Number> } ui_hub_port_i hub输出口下标
     *    @param { Promise<Number> } reci_i 接收卡下标
     * 返回:
     *    @returns { Promise<String> } 返回由字母和下标组成的字串; 字串下标值+1(即key值数字必须>=1);
     * 例子:
     *    NA
     * 备注:
     *    返回例子: S1_P1_H2
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-11
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_key_str(ui_send_i, ui_send_port, ui_hub_i, ui_hub_port_i, reci_i) {
        var str = "";
        var str_mark = "_";

        if ("number" === typeof ui_send_i) {
            str = "S" + (ui_send_i + 1);
        }

        if ("number" === typeof ui_send_port) {
            str += str_mark + "P" + (ui_send_port + 1);
        }

        if ("number" === typeof ui_hub_i) {
            str += str_mark + "H" + (ui_hub_i + 1);
        }

        if ("number" === typeof ui_hub_port_i) {
            str += str_mark + "P" + (ui_hub_port_i + 1);
        }

        if ("number" === typeof reci_i) {
            str += str_mark + "R" + (reci_i + 1);
        }

        return str;
    }


    // ************************************************************** mc_constructors_hw_card_data 读写数据接口********************************************************************* //
    /************************************************************************************************
     * 类型:
     *    读写接口
     * 功能:
     *    读写发送卡数据
     * 参数:
     *    @param { Promise<String> } str_io 读写字符串
     *    @param { Promise<Number> } idx 发送卡下标
     * 返回:
     *    @returns { Promise<Boolean> }
     *      1.写入时 true === 成功 || false 失败;
     *      2.读取时 Object === 发送卡对象 || false === 无数据
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-08
     *       内容 : 所有代码
    ************************************************************************************************/
    this.new_child_send = function (str_io, idx) {
        var b_io = trans_io_val(str_io);

        if ("number" !== typeof idx || null === b_io) {
            return false;
        }

        if (b_io) {
            return m_arr_card_data[idx];
        }

        var obj_data = g_util_foramt_data.new_obj_param(idx, "send", "send");

        if (!obj_data) {
            return false;
        }

        obj_data.KEY = get_key_str(idx);
        m_arr_card_data[idx] = obj_data;

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    读写接口
     * 功能:
     *    读写发送卡端口数据
     * 参数:
     *    @param { Promise<String> } str_io 读写字符串
     *    @param { Promise<Number> } send_idx 发送卡下标
     *    @param { Promise<Number> } port_idx 发送卡输出口
     * 返回:
     *    @returns { Promise<Boolean> }
     *      1.写入时 true === 成功 || false 失败;
     *      2.读取时 Object === 发送卡输出口对象 || false === 无数据
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-08
     *       内容 : 所有代码
    ************************************************************************************************/
    this.new_child_send_port = function (str_io, send_idx, port_idx) {
        var b_io = trans_io_val(str_io);

        if (null === b_io || "number" !== typeof send_idx || "number" !== typeof port_idx) {
            return false;
        }

        var cur_send = m_arr_card_data[send_idx];

        if (!cur_send) {
            return false;
        }

        var cur_send_port = cur_send.CHILD;

        if (!cur_send_port) {
            return false;
        }

        if (b_io) {
            return cur_send_port[port_idx];
        }

        var obj_data = g_util_foramt_data.new_obj_param(port_idx, "send_port", "send", true);

        if (!obj_data) {
            return false;
        }

        obj_data.KEY = get_key_str(send_idx, port_idx);
        cur_send_port[port_idx] = obj_data;

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    读写接口
     * 功能:
     *    读写hub数据
     * 参数:
     *    @param { Promise<String> } str_io 读写字符串
     *    @param { Promise<Number> } send_idx 发送卡下标
     *    @param { Promise<Number> } send_port_idx 发送卡输出口下标
     *    @param { Promise<Number> } hub_idx hub卡下标
     * 返回:
     *    @returns { Promise<Boolean> }
     *      1.写入时 true === 成功 || false 失败;
     *      2.读取时 Object === hub卡对象 || false === 无数据
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-08
     *       内容 : 所有代码
    ************************************************************************************************/
    this.new_child_hub = function (str_io, send_idx, send_port_idx, hub_idx) {
        var b_io = trans_io_val(str_io);

        if (null === b_io || "number" !== typeof send_idx || "number" !== typeof send_port_idx || "number" !== typeof hub_idx) {
            return false;
        }

        var cur_send = m_arr_card_data[send_idx];

        if (!cur_send) {
            return false;
        }

        var cur_send_port = cur_send.CHILD[send_port_idx];

        if (!cur_send_port) {
            return false;
        }

        if (b_io) {
            return cur_send_port.CHILD[hub_idx];
        }

        var obj_data = g_util_foramt_data.new_obj_param(hub_idx, "hub", "hub");

        if (!obj_data) {
            return false;
        }

        obj_data.KEY = get_key_str(send_idx, send_port_idx, hub_idx);
        cur_send_port.CHILD[hub_idx] = obj_data;

        return true;
    };

    /************************************************************************************************
     * 类型:
     *    读写接口
     * 功能:
     *    读写hub端口数据
     * 参数:
     *    @param { Promise<String> } str_io 读写字符串
     *    @param { Promise<Number> } send_idx 发送卡下标
     *    @param { Promise<Number> } port_idx 发送卡输出口下标
     *    @param { Promise<Number> } hub_idx hub卡下标
     *    @param { Promise<Number> } hub_port_idx hub卡输出口下标
     * 返回:
     *    @returns { Promise<Boolean> }
     *      1.写入时 true === 成功 || false 失败;
     *      2.读取时 Object === hub卡输出口对象 || false === 无数据
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-08
     *       内容 : 所有代码
    ************************************************************************************************/
    this.new_child_hub_port = function (str_io, send_idx, port_idx, hub_idx, hub_port_idx) {
        var b_io = trans_io_val(str_io);

        if (null === b_io || "number" !== typeof send_idx || "number" !== typeof port_idx || "number" !== typeof hub_idx || "number" !== typeof hub_port_idx) {
            return false;
        }

        var cur_send = m_arr_card_data[send_idx];

        if (!cur_send) {
            return false;
        }

        var cur_send_port = cur_send.CHILD[port_idx];

        if (!cur_send_port) {
            return false;
        }

        var cur_hub = cur_send_port.CHILD[hub_idx];

        if (!cur_hub) {
            return false;
        }

        if (b_io) {
            return cur_hub.CHILD[hub_port_idx];
        }

        var obj_data = g_util_foramt_data.new_obj_param(hub_port_idx, "hub_port", "hub", true);

        if (!obj_data) {
            return false;
        }

        obj_data.KEY = get_key_str(send_idx, port_idx, hub_idx, hub_port_idx);
        cur_hub.CHILD[hub_port_idx] = obj_data;

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    读写接口
     * 功能:
     *    读写接收卡数据(可以接受某端口下: 单条数据 || 全部接收卡数据)
     * 参数:
     *    @param { Promise<String> } str_io 读写字符串
     *    @param { Promise<Number> } str_port_type 该接受卡在的端口类型; 为发送卡时 === "SEND" || 为hub卡时 === "HUB"
     *    @param { Promise<Number> } arr_idx 卡下标数组; [发送卡, 发送卡输出口, hub卡, hub输出口]
     *    @param { Promise<Number> } b_all 是否为全部(读取时该参数无用)
     *    @param { Promise<Number> } num_idx (读取时该参数无用); 写入时分情况:具体参考备注;
     * 返回:
     *    @returns { Promise<Boolean> }
     *      1.写入时 true === 成功 || false 失败;
     *      2.读取时 Object === 参数端口下的所以接受卡数量 || false === 无数据
     * 例子:
     *    NA
     * 备注:
     *    写入时b_all, num_idx说明:
     *      1. b_all === true 时表示生成该端口下的全部接受卡; 则 num_idx 表示接受卡数量
     *      2. b_all === false 时表示生成该端口下单张接受卡; 则 num_idx 表示该输出口的接受卡位置下标(在哪一个位置生成);
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-08
     *       内容 : 所有代码
    ************************************************************************************************/
    this.new_child_receive = function (str_io, str_port_type, arr_idx, b_all, num_idx) {
        var b_io = trans_io_val(str_io);

        if (null === b_io || "string" !== typeof str_port_type || !mc_util_is_array(arr_idx)) {
            return false;
        }

        // 写入时参数判断
        if (!b_io) {
            if ("boolean" !== typeof b_all || "number" !== typeof num_idx) {
                return false;
            }
        }

        // card index
        var ui_send_i = Number(arr_idx[0]);
        var ui_send_port_i = Number(arr_idx[1]);
        var ui_hub_i = Number(arr_idx[2]);
        var ui_hub_port_i = Number(arr_idx[3]);

        // send card
        var cur_send = m_arr_card_data[ui_send_i];

        if (!cur_send) {
            return false;
        }

        var cur_send_port = cur_send.CHILD[ui_send_port_i];

        if (!cur_send_port) {
            return false;
        }

        switch (str_port_type.trim().toLocaleUpperCase()) {
        case "SEND":
            ui_hub_i = null;
            ui_hub_port_i = null;
            return new_recive_in_send_port();
        case "HUB":
            return new_recive_in_hub_port();
        default:
            return false;
        }

        // 修改接受卡key; param === 生成的接收卡数据数组
        function change_recive_card_key(param) {
            if (!param) {
                return false;
            }

            if (b_all) {
                param.forEach(function (item, idx) {
                    item.KEY = get_key_str(ui_send_i, ui_send_port_i, ui_hub_i, ui_hub_port_i, idx);
                });
            } else {
                param.KEY = get_key_str(ui_send_i, ui_send_port_i, ui_hub_i, ui_hub_port_i, num_idx);
            }

            return true;
        }

        // 发送卡端口下的接收卡
        function new_recive_in_send_port() {
            if (b_io) {
                return cur_send_port.CHILD;
            }

            //can set
            if (b_all) {
                var arr_data_send = g_util_foramt_data.new_obj_param(0, "receive", "receive", false, num_idx);

                if (!arr_data_send) {
                    return false;
                }

                change_recive_card_key(arr_data_send);
                cur_send_port.CHILD = arr_data_send;
            } else {
                var obj_data = g_util_foramt_data.new_obj_param(num_idx, "receive", "receive", false);

                if (!obj_data) {
                    return false;
                }

                change_recive_card_key(obj_data);
                cur_send_port.CHILD[num_idx] = obj_data;
            }

            return true;
        }

        // hub卡端口下的接收卡
        function new_recive_in_hub_port() {
            var cur_hub = cur_send_port.CHILD[ui_hub_i];

            if (!cur_hub) {
                return false;
            }

            var cur_hub_port = cur_hub.CHILD[ui_hub_port_i];

            if (!cur_hub_port) {
                return false;
            }

            if (b_io) {
                return cur_hub_port.CHILD;
            }

            // can set
            if (b_all) {
                var arr_data_hub = g_util_foramt_data.new_obj_param(0, "receive", "receive", false, num_idx);

                if (!arr_data_hub) {
                    return false;
                }

                change_recive_card_key(arr_data_hub);
                cur_hub_port.CHILD = arr_data_hub;
            } else {
                var obj_data_hub = g_util_foramt_data.new_obj_param(num_idx, "receive", "receive", false);

                if (!obj_data_hub) {
                    return false;
                }

                change_recive_card_key(obj_data_hub);
                cur_hub_port.CHILD[num_idx] = obj_data_hub;
            }

            return true;
        }
    };

    // ********************************************************************* mc_constructors_hw_card_data 其他接口 ********************************************************************* //
    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    从卡属性key的值获取数据对象
     * 参数:
     *    @param { Promise<String> } str_key key 字符串; 下标值从1开始; 因此返回下标需要-1
     * 返回:
     *    @returns { Promise<Object> } 保存的卡数据对象;
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-11
     *       内容 : 所有代码
    ************************************************************************************************/
    this.get_data_from_key = function (str_key) {
        if ("string" !== typeof str_key) {
            return false;
        }

        str_key = str_key.trim();
        var str_key_val = mc_replace_line_key_letters(str_key);

        str_key_val = str_key_val.replace(/R/g, "");

        var str_io = "get";
        var obj_data = null;
        var arr_val = str_key_val.split(":");
        var ui_len = arr_val.length;

        // add 1 && Number
        arr_val.forEach(function (item, idx, arr) {
            arr[idx] = Number(item) - 1;
        });

        // card index
        var ui_send_i = Number(arr_val[0]);
        var ui_send_port_i = Number(arr_val[1]);
        var ui_hub_i = Number(arr_val[2]);
        var ui_hub_port_i = Number(arr_val[3]);

        // receive card
        if (-1 !== str_key.indexOf("R")) {
            var arr_data_recive = [];

            // hub port
            switch (str_key.indexOf("H")) {
            case -1:
                arr_data_recive = this.new_child_receive(str_io, "SEND", arr_val);
                break;
            default:
                arr_data_recive = this.new_child_receive(str_io, "HUB", arr_val);
                break;
            }

            var obj_size_reci = g_util_foramt_data.get_size("RECEIVE");
            var ui_max = obj_size_reci.MAX || 3;

            if (ui_max < arr_data_recive.length) {
                obj_data = arr_data_recive;
            } else {
                obj_data = arr_data_recive[arr_val[ui_len - 1]];
            }

            return obj_data;
        }

        // send card || hub card
        switch (ui_len) {
        case 1:
            obj_data = this.new_child_send(str_io, ui_send_i);
            break;
        case 2:
            obj_data = this.new_child_send_port(str_io, ui_send_i, ui_send_port_i);
            break;
        case 3:
            obj_data = this.new_child_hub(str_io, ui_send_i, ui_send_port_i, ui_hub_i);
            break;
        case 4:
            obj_data = this.new_child_hub_port(str_io, ui_send_i, ui_send_port_i, ui_hub_i, ui_hub_port_i);
            break;
        default:
            break;
        }

        return obj_data;
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置图像信息，
     * 参数:
     *    @param { Promise<String> } obj 卡的数据对象;
     *    @param { Promise<String> } str_img 当前卡请求回来的图像信息字串
     * 返回:
     *    @returns { Promise<Boolean> } true = 设置成功 || 失败
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-11
     *       内容 : 所有代码
    ************************************************************************************************/
    this.set_img = function (obj, str_img) {
        if ("object" !== typeof obj || "string" !== typeof str_img) {
            return false;
        }

        var arr_img = mc_fm_img_rece(str_img);

        if (!arr_img) {
            return false;
        }

        obj.IMG = {
            X: arr_img[0],
            Y: arr_img[1],
            W: arr_img[2],
            H: arr_img[3]
        };

        return true;
    };
};


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    构建数据格式(读取硬件);
 * 参数:
 *    NA
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-06
 *       内容 : 所有代码
************************************************************************************************/
function mc_topo_format_data() { }

// define card mark string
mc_topo_format_data.prototype.card_mark_send = "SEND";
mc_topo_format_data.prototype.card_mark_hub = "HUB";
mc_topo_format_data.prototype.card_mark_receive = "RECEIVE";


// ********************************************************************* mc_topo_format_data 属性 ********************************************************************* //
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    构建数据格式(读取硬件);
 * 参数:
 *    @param { Promise<Number> } ui_index 该数据对象在父级数组下的下标
 *    @param { Promise<String> } str_card_name 卡名称; 数据格式 NAME 值;
 *    @param { Promise<String> } str_card_type  卡类型; 发送卡 || hub 卡 || 接收卡
 *    @param { Promise<Boolean> } b_port 是否为端口; true === 当前数为端口 || false === 不为端口
 *    @param { Promise<Number> } num  接收卡生成的数量 || 为空时,生成单个接收卡数据对象
 * 返回:
 *    @returns { Promise<Object> } 返回创建的数据对象 || 多个数据对象集合(此时为接收卡)
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-06
 *       内容 : 所有代码
************************************************************************************************/
mc_topo_format_data.prototype.new_obj_param = function (ui_index, str_card_name, str_card_type, b_port, num) {
    if ("number" !== typeof ui_index || "string" !== typeof str_card_name || "string" !== typeof str_card_type) {
        return false;
    }

    str_card_name = str_card_name.trim();
    str_card_type = str_card_type.trim();

    if ("" === str_card_name || "" === str_card_type) {
        return false;
    }

    var b_type = false;

    if (true === b_port) {
        b_type = true;
    }

    var str_card = "";

    switch (str_card_type.toLocaleUpperCase()) {
    case "SEND":
        str_card = "SEND";
        break;
    case "HUB":
        str_card = "HUB";
        break;
    case "RECEIVE":
        str_card = "RECEIVE";
        break;
    default:
        return false;
    }

    var str_key = str_card[0].toLocaleUpperCase() + ui_index;

    if (b_port) {
        str_key = "P" + ui_index;
    }

    var ui_idx = g_util_foramt_data.get_num();
    var str_id = new Date().getTime() + (Math.random() * 1000).toFixed(0);
    var obj = {
        INDEX: ui_index,
        NAME: str_card_name,
        ID: str_card_name + "_" + str_id + "_" + ui_idx,
        CARD: str_card,
        KEY: str_key
    };

    // 接收卡
    if ("RECEIVE" !== str_card) {
        obj.CHILD = [];
        obj.PORT = b_type;
        return obj;
    }

    // 多个接收卡
    if ("number" === typeof num) {
        var arr = [];

        for (var ui_i = 0; ui_i < num; ui_i++) {
            if (1 === num) {
                arr.push(obj);
                continue;
            }

            var str_key_reci = "R" + ui_i;
            var str_id_i = new Date().getTime() + (Math.random() * 1000).toFixed(0);
            var ui_idx_i = g_util_foramt_data.get_num();
            var obj_i = {
                INDEX: ui_i,
                NAME: str_card_name,
                ID: str_card_name + "_" + ui_i + "_" + str_id_i + "_" + ui_idx_i,
                CARD: str_card,
                KEY: str_key_reci
            };

            arr.push(obj_i);
        }

        return arr;
    }

    return obj;
};

mc_topo_format_data.prototype.get_id = function (obj_param) {
    if (!mc_util_is_object(obj_param)) {
        return false;
    }

    return obj_param.ID;
};

mc_topo_format_data.prototype.get_card = function (obj_param) {
    if (!mc_util_is_object(obj_param)) {
        return false;
    }

    return obj_param.CARD;
};

mc_topo_format_data.prototype.get_port = function (obj_param) {
    if (!mc_util_is_object(obj_param)) {
        return false;
    }

    return obj_param.PORT;
};

mc_topo_format_data.prototype.get_chi = function (obj_param) {
    if (!mc_util_is_object(obj_param)) {
        return false;
    }

    if (!obj_param.CHILD) {
        return false;
    }

    return obj_param.CHILD;
};

mc_topo_format_data.prototype.get_index = function (obj_param) {
    if (!mc_util_is_object(obj_param)) {
        return false;
    }

    return obj_param.INDEX;
};

// 定义数据模块大小
mc_topo_format_data.prototype.size = {
    "RECEIVE": {
        W: 70,
        H: 30,
        // 每个卡之间的间距
        STEP: 15,
        // 最大存放的接收卡个数
        MAX: 3
    },
    "SEND": {
        W: 0,
        H: 30,
        // 每个输出口间的间距
        STEP: 15,
        // 距离子集的值
        DIS: 50,
        PORT: {
            W: 30,
            H: 8
        }
    },
    "HUB": {
        W: 30,
        H: 0,
        // 距离子集的值
        DIS: 50
    },
    // 端口大小; X = 横向宽度; Y = 纵向宽度
    "PORT": {
        X: 30,
        Y: 8
    }
};
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取定义的模块宽高以及步进值
 * 参数:
 *    @param { Promise<String> } str_param 模块类别; "RECEIVE" || "SEND" || "HUB"
 * 返回:
 *    @returns { Promise<Object> } 定义的数据对象;具体可查看 mc_topo_format_data size属性 || false
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-08
 *       内容 : 所有代码
************************************************************************************************/
mc_topo_format_data.prototype.get_size = function (str_param) {
    if ("string" !== typeof str_param) {
        return false;
    }

    var obj_size = this.size[str_param];

    if (!obj_size) {
        return false;
    }

    return obj_size;
};

// 全局标识下标
mc_topo_format_data.prototype.num = 0;
mc_topo_format_data.prototype.clear_num = function () {
    this.num = 0;
    return;
};
mc_topo_format_data.prototype.get_num = function () {
    this.num++;
    return this.num;
};

// 缩放值
mc_topo_format_data.prototype.zoom = 1.2;
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    缩放尺寸; zoom 表示当前缩放尺寸; get_zoom方法返回缩放尺寸
 * 参数:
 *    @param { Promise<Number> } ui_type 缩放类别 1 === 放大 || 0 === 缩小 || defult === 上一个缩放值
 * 返回:
 *    @returns { Promise<Object> } 返回缩放值对象 { zoom : 需要缩放值; zoom_in: 不需要缩放的值  }
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-07
 *       内容 : 所有代码
************************************************************************************************/
mc_topo_format_data.prototype.get_zoom = function (ui_type) {
    if ("number" !== typeof ui_type) {
        // return false;
    }

    var ui_step = 0.02;

    switch (ui_type) {
    case 0:
        if (0.5 >= this.zoom) {
            this.zoom = 0.5;
            return this.zoom;
        }
        this.zoom -= ui_step;
        // return this.zoom;
        break;
    case 1:
        this.zoom += ui_step;
        // return this.zoom;
        break;
    default:
        break;
    }

    var ui_zoom = this.zoom;
    var ui_zoom_no = (1 / this.zoom);

    var obj_zoom = {
        zoom: ui_zoom,
        zoom_no: ui_zoom_no
    };

    return obj_zoom;
};


// ********************************************************************* mc_topo_format_data 属性 ********************************************************************* //


// 格式化数据，单个卡数据格式
function mc_topo_foramt_card_idx_data(idx, port_idx) {
    // 卡下标
    this.idx = idx;
    // 卡输出口下标
    this.port_idx = port_idx;
}


// ********************************************************************* 硬件读取 ********************************************************************* //
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取单个hub口下的多个接收卡连接;
 * 参数:
 *    @param { Promise<String> } obj_send_data 发送卡数据 为 mc_topo_foramt_card_idx_data 实例
 *    @param { Promise<String> } obj_hub_data hub卡数据  为 mc_topo_foramt_card_idx_data 实例; 内 输出口可为空
 *    @param { Promise<String> } hub_port_num hub输出口数量;
 *    @param { Promise<String> } res_type 返回类别
 *    @param { Promise<String> } callback 回调函数
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-08
 *       内容 : 所有代码
************************************************************************************************/
function mc_get_multi_recvcard_connect(obj_send_data, obj_hub_data, hub_port_num, res_type, callback) {
    // HW_RECVCARD_CONNECT
    if (!mc_util_is_object(obj_send_data) || !mc_util_is_object(obj_hub_data) || "function" !== typeof callback) {
        return;
    }

    if ("number" !== typeof hub_port_num) {
        return;
    }

    if (0 === hub_port_num) {
        callback(false);
    }


    var obj_sdk_param = new mc_sdk_param();

    obj_sdk_param.set_param_clear();
    obj_sdk_param.set_cmd("GET_PARAM");
    obj_sdk_param.set_func("HW_RECVCARD_CONNECT");

    var ui_len = hub_port_num;
    var str_send = obj_send_data.idx + ":" + obj_send_data.port_idx;
    var arr_name_id = [];

    for (var ui_i = 0; ui_i < ui_len; ui_i++) {
        var str_hub = obj_hub_data.idx + ":" + ui_i;
        var str_val = "(" + str_send + ":" + str_hub + ")";
        var str_name_id = "mark:" + str_val;

        arr_name_id.push(str_name_id);
        obj_sdk_param.set_param_value(str_name_id, str_val);
    }

    var str_json = obj_sdk_param.get_json();

    $.post("/access_shell", str_json, function (response, status) {
        if ("success" !== status) {
            callback(false);
            return;
        }
        var str_err = obj_sdk_param.get_param_value("ERROR_CODE");
        var error_code = "0x00000000";

        if (error_code === str_err || !str_err) {
            callback(false, response);
            return;
        }

        obj_sdk_param.set_param_clear();
        obj_sdk_param.set_json(response);

        var arr_res = [];

        for (var i = 0; i < ui_len; i++) {
            var item = arr_name_id[i];
            var str_json_val = obj_sdk_param.get_attr_val(item, "STATUS");
            var obj_deal_val = mc_get_recvcard_msg(str_json_val);

            arr_res.push({
                name_id: item,
                value: obj_deal_val
            });
        }

        callback(arr_res);

        return;
    });
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取发送卡图像配置（多个发送卡）
 * 参数:
 *    @param { Promise<String> } str_io 读写字串; 默认读取; === "set"时为写入
 *    @param { Promise<String> } arr_send_card 发送卡数组; 内对象[{ INDEX:发送卡下标, VALUE:"" }]
 *    @param { Promise<String> } callback 回调函数;
 * 返回:
 *    @returns { Promise<Boolean> } false === 参数格式错误；
 * 例子:
 *    NA
 * 备注:
 *    回调参数(error, response)
 *    1. error === true 表示请求错误； || false === 成功
 *    2. response 请求字串值 || undefine
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-11
 *       内容 : 所有代码
************************************************************************************************/
function mc_get_hw_send_card_img_rect(str_io, arr_send_card, callback) {
    // HW_SENDCARD_IMG_RECT
    if ("function" !== typeof callback || !mc_util_is_array(arr_send_card)) {
        return false;
    }

    var obj_sdk_param = new mc_sdk_param();

    obj_sdk_param.set_param_clear();
    obj_sdk_param.set_cmd("GET_PARAM");

    if ("set" === str_io) {
        obj_sdk_param.set_cmd("SET_PARAM");
    }

    obj_sdk_param.set_func("HW_SENDCARD_IMG_RECT");

    // HW_SENDCARD_OUTPORT_IMG_RECT

    var ui_len = arr_send_card.length;

    if (0 === ui_len) {
        callback(true);
        return true;
    }

    for (var idx = 0; idx < ui_len; idx++) {
        var item = arr_send_card[idx];

        if (!mc_util_is_object(item)) {
            continue;
        }

        var str_idx = item.INDEX;

        if ("string" !== typeof str_idx) {
            item.INDEX = str_idx.toString();
        }

        var str_param_name = item.INDEX;

        obj_sdk_param.set_param_value(str_param_name, item.INDEX);
    }

    var str_json = obj_sdk_param.get_json();

    $.post("/access_shell", str_json, function (response, status) {
        if ("success" !== status) {
            callback(true);
            return true;
        }

        obj_sdk_param.set_param_clear();
        obj_sdk_param.set_json(response);

        for (let ui_res_i = 0; ui_res_i < ui_len; ui_res_i++) {
            var item_res = arr_send_card[ui_res_i];

            if (!mc_util_is_object(item_res)) {
                continue;
            }


            var str_img_msg = obj_sdk_param.get_param_value(item_res.INDEX);

            item_res.VALUE = str_img_msg;
        }

        callback(false, arr_send_card);
        return true;
    });

    return true;
}


// ********************************************************************* 渲染 ********************************************************************* //
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    构造页面块
 * 参数:
 *    @param { Promise<Array> } arr 读取硬件构造的卡数据数据
 *    @param { Promise<Function> } callback 回调函数
 * 返回:
 *    @returns { Promise<Boolean> } false === 参数错误
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-13
 *       内容 : 所有代码
************************************************************************************************/
function mc_topo_create_block(arr, callback) {
    if (!mc_util_is_array(arr) || "function" !== typeof callback) {
        return false;
    }

    // define data
    var str_calss_def = "str_calss_def";
    var obj_size_send = g_util_foramt_data.get_size("SEND");
    var obj_size_hub = g_util_foramt_data.get_size("HUB");
    var obj_size_reci = g_util_foramt_data.get_size("RECEIVE");

    var param = {
        id: "wrap",
        idx: 0
    };

    // 测试数据
    var ui_item_idx = 0;

    // id === "mc_hw_topo_container"
    var obj_wrap = document.getElementById(g_id_canvas_topo);
    var m_util_style = new mc_format_style();

    recur_create_card_and_port(param, arr);
    mc_hw_topo_connect_line(arr);


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    递归构造卡，端口； recur
     * 参数:
     *    @param { Promise<Object> } obj 父级数据
     *    @param { Promise<Array> } arr_chi 子集数组
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function recur_create_card_and_port(obj, arr_chi) {
        var len = arr_chi.length;

        for (var ui_i = 0; ui_i < len; ui_i++) {
            var item = arr_chi[ui_i];

            if (!item) {
                continue;
            }

            if (!item.CHILD) {
                // 接收卡
                if ("RECEIVE" === item.CARD) {
                    var $obj_con_hub = get_card_container(obj);

                    if ($obj_con_hub) {
                        var str_receive_html = create_recevie_card(item, obj, ui_i, len);
                        var ui_max = obj_size_reci.MAX || 3;

                        if (ui_max < len) {
                            $obj_con_hub.container.append(str_receive_html);
                            return;
                        }

                        $obj_con_hub.container.append(str_receive_html);
                    }
                }
                continue;
            }

            var chi_len = item.CHILD.length;

            if (0 === chi_len) {
                continue;
            }


            if (false === item.PORT) {
                // 创建发送卡块以及外框布局;
                if ("SEND" === item.CARD) {
                    var str_send_wrap = create_send_card(item, ui_i);

                    obj_wrap.innerHTML += str_send_wrap;
                }

                if ("HUB" === item.CARD) {
                    var $obj_con = get_card_container(obj, "HUB");

                    if ($obj_con) {
                        var str_hub_html = create_hub_card(item, ui_i);

                        $obj_con.container.append(str_hub_html);
                    }
                }
            }

            // item = 下一级的父级数据
            recur_create_card_and_port(item, item.CHILD);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建发送卡块
     * 参数:
     *    @param { Promise<Object> } item 数据对象；
     *    @param { Promise<Number> } ui_idx 发送卡下标
     * 返回:
     *    @returns { Promise<String> } html 字符串
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function create_send_card(item, ui_idx) {
        var str_id = item.ID;
        var str_card = item.CARD;
        var ui_chi = item.CHILD.length;

        // 单个发送卡输出口步进值
        var ui_w_each_port = (obj_size_reci.W + obj_size_reci.STEP) * obj_size_reci.MAX + obj_size_hub.DIS + (obj_size_reci.W);
        var ui_width = ui_chi * ui_w_each_port + obj_size_hub.W - ui_w_each_port;

        // set send card style
        m_util_style.clear_data();
        m_util_style.set_val("width", ui_width);
        var str_style = m_util_style.get_style();

        // 端口
        var arr_port = [];
        var ui_step = ui_w_each_port;
        var ui_port_w = obj_size_hub.W;
        // 输出口外部包裹子集
        var arr_chi_wrap = [];
        // port class
        var str_class_port = [str_calss_def, "topo_send_port"].join(" ");

        for (var ui_i = 0; ui_i < ui_chi; ui_i++) {
            var item_id = item.CHILD[ui_i].ID;
            // parent width === send port step width
            var ui_left = ui_i * ui_step;

            // set send card port style
            m_util_style.clear_data();
            m_util_style.set_val("left", ui_left);
            m_util_style.set_val("width", ui_port_w);

            var str_port_hrml_i = "<div id=" + item_id + " key=" + item.CHILD[ui_i].KEY + " class='" + str_class_port + "' style=" + m_util_style.get_style() + "></div>";

            arr_port.push(str_port_hrml_i);

            var str_mark = "send_port_" + ui_i;
            var str_card_port_wrap = "<div class='send_port_child_wrap' style='width: " + ui_w_each_port + "px;' mark=" + str_mark + "></div>";

            arr_chi_wrap.push(str_card_port_wrap);
        }

        var ui_idx_set_one = ui_idx + 1;
        var str_val_html = get_card_inner_val_html(str_card, "S" + ui_idx_set_one);
        // card class
        var str_class = [str_calss_def, "topo_send"].join(" ");
        var html = "<div class='one_item_wrap' item_idx=" + ui_idx_set_one + " idx=" + ui_item_idx + " style=''><div id=" + str_id + " key=" + item.KEY + " class='" + str_class + "' style=" + str_style + ">" + str_val_html + "<div class='send_port_wrap'>" + arr_port.join("") + "</div></div><div class='no_send_card_wrap'>" + arr_chi_wrap.join("") + "</div></div>";

        // 测试
        ui_item_idx++;
        return html;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建hub卡块
     * 参数:
     *    @param { Promise<Object> } item 当前数据对象；
     *    @param { Promise<Number> } ui_idx hub卡下标
     * 返回:
     *    @returns { Promise<String> } html 字符串
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function create_hub_card(item, ui_idx) {
        var str_id = item.ID;
        var str_card = item.CARD;
        var ui_chi = item.CHILD.length;
        var ui_height = ui_chi * (obj_size_reci.H + (obj_size_reci.H / 2)) - (obj_size_reci.H / 2);
        // 距离上一个hub卡间距
        var ui_top = obj_size_send.DIS;

        // set hub card style
        m_util_style.clear_data();
        m_util_style.set_val("height", ui_height);

        if (0 !== ui_idx) {
            m_util_style.set_val("margin-top", ui_top);
        }

        var str_style = m_util_style.get_style();

        // 端口
        var arr_port = [];
        var ui_step = 45;
        var ui_port_H = obj_size_reci.H;
        // port class
        var str_class_port = [str_calss_def, "topo_hub_port"].join(" ");

        for (var ui_i = 0; ui_i < ui_chi; ui_i++) {
            var item_id = item.CHILD[ui_i].ID;
            var ui_left = ui_i * ui_step;

            // set hub  port style
            m_util_style.clear_data();
            m_util_style.set_val("top", ui_left);
            m_util_style.set_val("height", ui_port_H);

            var str_port_hrml_i = "<div id=" + item_id + " key=" + item.CHILD[ui_i].KEY + " class='" + str_class_port + "' style=" + m_util_style.get_style() + "></div>";

            arr_port.push(str_port_hrml_i);
        }

        var ui_i_set_one = ui_idx + 1;
        var str_val_html = get_card_inner_val_html(str_card, "H" + ui_i_set_one);
        // card class
        var str_class = [str_calss_def, "topo_hub"].join(" ");
        var html = "<div id=" + str_id + " key=" + item.KEY + " class='" + str_class + "' style=" + str_style + ">" + str_val_html + "<div class='hub_port_wrap' >" + arr_port.join("") + "</div>" + "<div class='hub_port_reci_wrap'></div>" + "</div>";

        return html;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建接收卡块
     * 参数:
     *    @param { Promise<Object> } item 当前数据对象；
     *    @param { Promise<Object> } obj_p 父级数据对象
     *    @param { Promise<Number> } ui_idx 接收卡卡下标
     *    @param { Promise<Number> } ui_len 当前端口下的接收卡总数
     * 返回:
     *    @returns { Promise<String> } html 字符串
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function create_recevie_card(item, obj_p, ui_idx, ui_len) {
        // style_p === 父级样式;
        var style_p = $("#" + obj_p.ID)[0].style;
        var str_id = item.ID;
        var str_card = item.CARD;
        var ui_each_w = obj_size_reci.W;
        var ui_step = obj_size_reci.STEP;
        var str_style = "";

        m_util_style.clear_data();

        // send port recevie
        if ("SEND" === obj_p.CARD) {
            // this is in send port
            var ui_top = ui_step;

            if (0 === ui_idx) {
                ui_top = 0;
            }

            m_util_style.set_val("margin-top", ui_top);
            m_util_style.set_val("position", "relative", null);
            str_style = m_util_style.get_style();
        } else {
            var ui_left = ui_idx * (ui_each_w + ui_step);

            m_util_style.set_val("left", ui_left);
            m_util_style.set_val("top", style_p.top, null);
            str_style = m_util_style.get_style();
        }

        // multi card
        var str_multi = "";
        var ui_i_set_one = (ui_idx + 1);
        var str_name = "R" + ui_i_set_one;
        var ui_max = obj_size_reci.MAX || 3;

        if (ui_max < ui_len) {
            str_multi = "multi";
            str_name = "R" + "(" + ui_i_set_one + "-" + ui_len + ")";
        }

        // card class
        var str_class = [str_calss_def, "topo_receive", str_multi].join(" ");
        var str_val_html = get_card_inner_val_html(str_card, str_name);

        m_util_style.clear_data();
        m_util_style.set_val("width", ui_each_w);

        var html = "<div class='recevie_card_warp' style=" + str_style + "><div id=" + str_id + " key=" + item.KEY + " class='" + str_class + "' style=" + m_util_style.get_style() + ">" + str_val_html + "</div></div>";

        return html;
    }


    callback();
    return true;
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取卡的父级容器; 与dom布局一致
 * 参数:
 *    @param { Promise<Object> } obj_p 父级数据对象
 * 返回:
 *    @returns { Promise<Object> } 返回容器对象；{ container: 容器； style: 父级样式 } || false = 找不到
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-13
 *       内容 : 所有代码
************************************************************************************************/
function get_card_container(obj_p) {
    var str_id_p = obj_p.ID;
    var str_card_p = obj_p.CARD;
    var ui_send_port = obj_p.INDEX;
    var $obj_p = $("#" + str_id_p);

    if (0 === $obj_p.length) {
        return false;
    }

    var obj = {
        container: null,
        style: $obj_p[0].style
    };

    if (0 === $obj_p.length) {
        return false;
    }

    if ("HUB" === str_card_p) {
        // hub_port_wrap
        var obj_reci_wrap = $obj_p.parents(".hub_port_wrap").siblings(".hub_port_reci_wrap");

        obj.container = obj_reci_wrap;
        return obj;
    }

    var $obj_wrap = $obj_p.parents(".one_item_wrap");

    if (0 === $obj_wrap.length) {
        return false;
    }

    var $obj_is_not_send = $obj_wrap.find(".no_send_card_wrap");

    if (0 === $obj_is_not_send.length) {
        return false;
    }

    var $obj_send_port_idx = $obj_is_not_send.children(".send_port_child_wrap").eq(ui_send_port);

    if (0 === $obj_send_port_idx.length) {
        return false;
    }

    obj.container = $obj_send_port_idx;
    return obj;
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取卡内标识名称html字串
 * 参数:
 *    @param { Promise<String> } str_card 表示的卡类别(数据对象存储为"CARD"的属性值)"SEND" || "HUB" || "RECEIVE"
 *    @param { Promise<String> } str_val 卡显示名称; eg： "S1" || "H2"
 * 返回:
 *    @returns { Promise<String> } 返回card 显示名称的html字符串
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-13
 *       内容 : 所有代码
************************************************************************************************/
function get_card_inner_val_html(str_card, str_val) {
    if ("string" !== typeof str_card || "string" !== typeof str_val) {
        return "";
    }

    var str_name = str_val.trim();
    var str_html = "<span card_type=" + str_card + " card_name=" + str_name + ">" + str_name + "</span>";

    return str_html;
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    初始化布局; 对齐svg画布宽高 && 更新缩放尺寸
 * 参数:
 *    NA
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-13
 *       内容 : 所有代码
************************************************************************************************/
function init_svg_size() {
    var obj_canvas_container = document.getElementById(g_id_canvas_container);
    var obj_canvas_topo = document.getElementById(g_id_canvas_topo);
    var obj_canvas_line = document.getElementById(g_id_canvas_line);

    if (!obj_canvas_container || !obj_canvas_topo || !obj_canvas_line) {
        return;
    }

    var ui_w = obj_canvas_topo.offsetWidth;
    var ui_h = obj_canvas_topo.offsetHeight;

    obj_canvas_line.setAttribute("width", ui_w + 50);
    obj_canvas_line.setAttribute("height", ui_h + 50);

    // 初始化缩放尺寸
    mc_zoom_mousewheel_callback();
}


// ********************************************************************* 连线 ********************************************************************* //
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    端口和子集连线
 * 参数:
 *    @param { Promise<Array> } arr_data 拓扑图数据数组
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-13
 *       内容 : 所有代码
************************************************************************************************/
function mc_hw_topo_connect_line(arr_data) {
    if (!mc_util_is_array(arr_data)) {
        return;
    }

    if (0 === arr_data.length) {
        return;
    }

    var obj_line_svg = document.getElementById(g_id_canvas_line);

    if (!obj_line_svg) {
        return;
    }

    clear_line(obj_line_svg);
    loop_to_line(arr_data);


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    轮询连线
     * 参数:
     *    @param { Promise<Array> } arr_chi 子集数据
     *    @param { Promise<Object> } obj_parent 父级数据；只有端口的子集有父级数据；
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function loop_to_line(arr_chi, obj_parent) {
        if (!mc_util_is_array(arr_chi)) {
            return;
        }

        var len = arr_chi.length;

        if (0 === len) {
            return;
        }

        var obj_parent_pos = null;

        for (var ui_i = 0; ui_i < len; ui_i++) {
            var item = arr_chi[ui_i];

            if (!item) {
                continue;
            }

            var str_card = item.CARD;
            var str_port = item.PORT;


            // 执行条件
            var b_type_param_par = "object" === typeof obj_parent;
            var b_hub = false === str_port && b_type_param_par;
            var b_reci = "RECEIVE" === str_card && b_type_param_par;


            if (b_hub || b_reci) {
                // 可连线

                // 父级位置值
                if (null === obj_parent_pos) {
                    obj_parent_pos = get_connect_point_pos(obj_parent, true, 3);
                }

                if (obj_parent_pos) {
                    hw_connect_two_by_two(obj_parent, item, obj_parent_pos);
                }
            }

            if (true === str_port) {
                var obj_param = item;

                loop_to_line(item.CHILD, obj_param);
            } else {
                loop_to_line(item.CHILD);
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    两两连线
     * 参数:
     *    @param { Promise<String> } o_parent 父级数据(连线开始点)
     *    @param { Promise<String> } o_child  子级数据（连线结束点）
     *    @param { Promise<String> } o_pos_parent 父级dom连接点位置
     * 返回:
     *    @returns { Promise<Object> } 父级dom连接点位置 || false === 参数失败(不存在连线块)
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function hw_connect_two_by_two(o_parent, o_child, o_pos_parent) {
        if ("object" !== typeof o_parent || "object" !== typeof o_child || "object" !== typeof o_pos_parent) {
            return false;
        }

        // var str_parent_ca
        var id_par = g_util_foramt_data.get_id(o_parent);
        var id_chi = g_util_foramt_data.get_id(o_child);
        var obj_chi = document.getElementById(id_chi);

        if (!obj_chi) {
            return false;
        }

        // 位置值
        var obj_res_chi = get_connect_point_pos(o_child, false, 0);
        var obj_res_par = o_pos_parent;

        // console.log("[偏差值]", obj_res_par, obj_res_chi);

        if (!obj_res_par || !obj_res_chi) {
            return false;
        }

        var str_lin_id = id_par + "__" + id_chi;

        var x1 = obj_res_par.left;
        var y1 = obj_res_par.top;

        // var x2 = obj_res_par.left;
        var x2 = obj_res_chi.left;
        var y2 = obj_res_chi.top;

        // 矫正位置; 结束点对用开始点位置
        var str_card_type = g_util_foramt_data.get_card(o_parent);

        if ("SEND" === str_card_type) {
            x2 = obj_res_par.left;
        }

        if ("HUB" === str_card_type) {
            y2 = obj_res_par.top;
        }

        var obj_line_res = mc_construct_line(str_lin_id, x1, y1, x2, y2);

        if (obj_line_res) {
            // change line style
            obj_line_res.svg_line.setAttribute("stroke-width", "1");
            obj_line_res.svg_line.setAttribute("stroke", "yellow");
            obj_line_res.svg_line.setAttribute("marker-end", "");
            // obj_line_svg.appendChild(obj_line_res.svg_marker);
            obj_line_svg.appendChild(obj_line_res.svg_line);
        }

        return obj_res_par;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取相对最外层位置值(具体的位置值)
     * 参数:
     *    @param { Promise<Object> } obj 当前块对象;
     *    @param { Promise<Object> } offset 父级偏差值(递归时使用);可无
     * 返回:
     *    @returns { Promise<String> } 参数1
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-07
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_offset_parent(obj, offset) {
        var obj_parent = obj.offsetParent;
        var obj_off = {
            left: obj.offsetLeft,
            top: obj.offsetTop
        };

        if (offset) {
            obj_off.left += offset.left;
            obj_off.top += offset.top;
        }

        // 拓扑图最外层容器 mc_hw_topo_container === g_id_canvas_topo
        if (g_id_canvas_topo === obj_parent.id) {
            return obj_off;
        }

        var obj_res = get_offset_parent(obj_parent, obj_off);

        return obj_res;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取块连接点位置; 可返回的连接点有：块中心点和四个顶点；
     * 参数:
     *    @param { Promise<Object> } obj_data 块对象;
     *    @param { Promise<Boolean> } b_center 是否为中心点
     *    @param { Promise<Number> } ui_dir 方向 0.1.2.3 ; 从左上角开始顺时针；分别表示上右下左各顶点
     * 返回:
     *    @returns { Promise<String> } { left, top }返回连接点位置 || false === 参数错误
     * 例子:
     *    NA
     * 备注:
     *    ui_dir 顶点参数说明:
     *       0 === 左上角
     *       1 === 右上角
     *       2 === 右下角
     *       3 === 左下角
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-07
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_connect_point_pos(obj_data, b_center, ui_dir) {
        if ("object" !== typeof obj_data || "boolean" !== typeof b_center) {
            return false;
        }

        var str_id = g_util_foramt_data.get_id(obj_data);
        var obj = document.getElementById(str_id);

        if (!obj) {
            return false;
        }

        var ui_w = obj.offsetWidth;
        var ui_h = obj.offsetHeight;
        var obj_pos = get_offset_parent(obj);

        var obj_res = {};

        if (b_center) {
            obj_res.left = obj_pos.left + (ui_w / 2);
            obj_res.top = obj_pos.top + (ui_h / 2);

            return obj_res;
        }

        if ("number" !== typeof ui_dir) {
            return false;
        }

        switch (ui_dir) {
        case 0:
            obj_res = obj_pos;
            break;
        case 1:
            obj_res.left = obj_pos.left + ui_w;
            obj_res.top = obj_pos.top;
            break;
        case 2:
            obj_res.left = obj_pos.left + ui_w;
            obj_res.top = obj_pos.top + ui_h;
            break;
        case 3:
            obj_res.left = obj_pos.left;
            obj_res.top = obj_pos.top + ui_h;
            break;
        default:
            return false;
        }

        return obj_res;
    }

    function clear_line() {
        $("#" + g_id_canvas_line + " line").remove();
    }
}


// ********************************************************************* 滚动条事件 ********************************************************************* //
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    缩放调用;
 * 参数:
 *    @param { Promise<Number> } ui_type 缩放类别 1 === 放大 || 0 === 缩小 || 默认 = 获取上一个缩放值
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-07
 *       内容 : 所有代码
************************************************************************************************/
function mc_zoom_mousewheel_callback(ui_type) {
    var obj_layer_scale = document.getElementById(g_id_layer_scale);
    var obj_zoom = null;

    switch (ui_type) {
    case 0:
        // console.log("[缩小]");
        obj_zoom = g_util_foramt_data.get_zoom(0);
        break;
    case 1:
        // console.log("[放大]");
        obj_zoom = g_util_foramt_data.get_zoom(1);
        break;
    default:
        obj_zoom = g_util_foramt_data.get_zoom();
        break;
    }

    var ui_zoom = obj_zoom.zoom;
    var ui_zoom_no = obj_zoom.zoom_no;

    obj_layer_scale.style.transform = "scale(" + ui_zoom + " )";

    // var arr_name_val = $("#" + g_id_canvas_container + " [card_name]");
    // var span_1 = arr_name_val[0];
    // var old_color = span_1;

    var str_def_trans = "translate(-50%, -50%)";

    $("#" + g_id_canvas_container + " [card_name]").css({
        transform: str_def_trans + "scale(" + ui_zoom_no + " )"
    });
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    初始化事件
 * 参数:
 *    NA
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-11
 *       内容 : 所有代码
************************************************************************************************/
function init_event() {
    // $(".topo_send, .topo_hub, .topo_receive, .topo_send_port, .topo_hub_port")
    $(".topo_send, .topo_hub, .topo_send_port, .topo_hub_port").hover(function e_in(event) {
        // currentTarget
        var obj_tag = event.target;
        // var obj_cur_tag = event.currentTarget;

        if ("mouseenter" === event.type) {
            // 接收卡
            if ("SPAN" === obj_tag.tagName) {
                var o_parent = $(obj_tag).parents(".topo_receive");

                if (0 !== o_parent.length) {
                    return;
                }
            } else {
                if (obj_tag.className) {
                    if (-1 !== obj_tag.className.indexOf("topo_receive")) {
                        return;
                    }
                }
            }
        }

        var str_key = obj_tag.getAttribute("key");

        if ("mouseleave" === event.type) {
            var from_obj = event.fromElement || event.relatedTarget;
            var to_obj = event.toElement || event.target;
            // 输出口鼠标移出时 && 移到父级

            if (from_obj) {
                str_key = to_obj.getAttribute("key");
            }
        }


        var res_data = g_obj_instance_card_data.get_data_from_key(str_key);

        if (res_data) {
            if (mc_util_is_array(res_data)) {
                // // 所有接收卡
            } else {
                var obj_img = res_data.IMG;
                var str_cur = g_obj_lang.get_item_val("MC_LANG_CURRENT_INDEX") + ": " + (res_data.KEY).bold() + ";";

                if (obj_img) {
                    // img
                    var str_port = res_data.PORT ? g_obj_lang.get_item_val("MC_LANG_CARD_PORT") : " ";
                    var str = "</br>" + str_port + g_obj_lang.get_item_val("MC_LANG_CARD_IMG") + ": " + g_obj_lang.get_item_val("MC_LANG_SIZE") + " (" + obj_img.W + "X" + obj_img.H + "); " + g_obj_lang.get_item_val("MC_LANG_POS") + " (" + obj_img.X + "," + obj_img.Y + ")";

                    str_cur += str;
                }

                mc_topo_show_msg(event, str_cur);
            }
            return;
        }

        mc_topo_show_msg(event, "");
    });


    // receive card
    $(".topo_receive").hover(receive_card_hover);

    function receive_card_hover(event) {
        var obj_tag = event.currentTarget;
        var str_key = obj_tag.getAttribute("key");

        if ("mouseleave" === event.type) {
            mc_topo_show_msg(event, "");
            return;
        }

        var res_data = g_obj_instance_card_data.get_data_from_key(str_key);

        if (!res_data) {
            mc_topo_show_msg(event, "");
            return;
        }

        if (mc_util_is_array(res_data)) {
            // 所有接收卡
            var str_text = obj_tag.innerText;
            var ui_r_i = str_key.lastIndexOf("_");
            var str_port = str_key.substring(0, (ui_r_i + 1));
            var str_cur = g_obj_lang.get_item_val("MC_LANG_CURRENT_INDEX") + ": " + (str_port).bold() + str_text + ";";

            mc_topo_show_msg(event, str_cur);

            // var ui_len = res_data.length;
            // var arr_str = [];

            // for (let ui_i = 0; ui_i < ui_len; ui_i++) {
            //     const obj_i = res_data[ui_i];

            //     if (obj_i) {
            //         var str_i = "<div class='topo_show_msg'>" + obj_i.KEY + "</div>";

            //         arr_str.push(str_i);
            //     }
            // }
            // mc_topo_show_msg(event, arr_str.join(""));
        } else {
            var str_cur_single = g_obj_lang.get_item_val("MC_LANG_CURRENT_INDEX") + ": " + res_data.KEY + ";";

            mc_topo_show_msg(event, str_cur_single);
        }
    }
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    显示信息
 * 参数:
 *    @param { Promise<String> } event 鼠标事件参数
 *    @param { Promise<String> } str 显示的信息字串
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-11
 *       内容 : 所有代码
************************************************************************************************/
function mc_topo_show_msg(event, str) {
    var obj_msg = document.getElementById("topo_show_msg");

    if (!str) {
        obj_msg.innerText = null;
        $(obj_msg).css({
            display: "none",
            position: "fixed",
            left: "",
            top: ""
        });
        return;
    }


    obj_msg.innerHTML = str;
    event = event || window.event;

    var ui_left = event.clientX + 10;
    var ui_top = event.clientY + 10;

    $(obj_msg).css({
        display: "block",
        position: "fixed",
        left: ui_left,
        top: ui_top
    });
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取发送卡请求图像参数数组
 * 参数:
 *    @param { Promise<Number> } num 发送卡数量
 * 返回:
 *    @returns { Promise<Array> } 请求图像数组
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-13
 *       内容 : 所有代码
************************************************************************************************/
function mc_get_send_card_req_img_array(num) {
    if ("number" !== typeof num) {
        return false;
    }

    var arr = [];

    if (0 === num) {
        return arr;
    }

    for (var ui_i = 0; ui_i < num; ui_i++) {
        var obj = {
            INDEX: ui_i.toString(),
            VALUE: ""
        };

        arr.push(obj);
    }

    return arr;
}


// ********************************************************************* 构造数据 ********************************************************************* //
/************************************************************************************************
 * 类型:
 *    回调函数
 * 功能:
 *   读取发送卡连接回调
 * 参数:
 *    @param { Promise<String> } res_send 硬件发送卡数据
 * 返回:
 *    @returns { Promise<Boolean> } false = 参数错误
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-12
 *       内容 : 所有代码
************************************************************************************************/
function mc_callback_sand_card_connect(res_send) {
    if ("string" !== typeof res_send || 0 === res_send.length) {
        return false;
    }

    // clear data
    g_obj_instance_card_data.clear_data();
    g_util_foramt_data.clear_num();

    // 发送卡总数
    var ui_send_card_total_num = 0;
    var obj_sdk_param = new mc_sdk_param();

    obj_sdk_param.set_param_clear();
    obj_sdk_param.set_json(res_send);
    var ui_param_cnt = obj_sdk_param.get_param_cnt();
    var str_code_name = obj_sdk_param.get_param_name(0);

    if (1 === ui_param_cnt && "ERROR_CODE" === str_code_name) {
        return false;
    }

    // has send card
    ui_send_card_total_num = ui_param_cnt;

    parent.mc_loading_wait();

    // 获取发送卡子集数据
    get_send_card_child(function () {
        // 创建块
        mc_topo_create_block(g_obj_instance_card_data.get_data(), function () {
            init_svg_size();
            init_event();
            parent.mc_loading_wait_remove();
            // console.log("[实例对象数据]", g_obj_instance_card_data.get_data());
        });
    });


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取发送卡子集数据
     * 参数:
     *    @param { Promise<String> } callback 回调函数
     * 返回:
     *    @returns { Promise<Boolean> } false === 参数错误
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-01-13
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_send_card_child(callback) {
        if ("function" !== typeof callback) {
            return false;
        }

        loop_send(0, ui_send_card_total_num);

        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    发送卡
         * 参数:
         *    @param { Promise<String> } ui_idx 发送卡下标
         *    @param { Promise<String> } ui_total 发送卡总数
         * 返回:
         *    NA
         * 例子:
         *    NA
         * 备注:
         *    NA
         * 修改:
         *    1. 类型 : 创建
         *       作者 : 巫昭雯
         *       时间 : 2021-01-13
         *       内容 : 所有代码
        ************************************************************************************************/
        function loop_send(ui_idx, ui_total) {
            if (ui_idx < ui_total) {
                g_obj_instance_card_data.new_child_send("set", ui_idx);

                var str_param_name = obj_sdk_param.get_param_name(ui_idx);
                var str_param_val = obj_sdk_param.get_param_value(str_param_name);

                if (str_param_val && -1 !== str_param_val.indexOf(":")) {
                    // 输出口个数
                    var child_num = Number(str_param_val.split(":")[1]);
                    var obj_send_card_idx_data = new mc_topo_foramt_card_idx_data(ui_idx, null);

                    get_send_card_port_child(child_num, obj_send_card_idx_data);
                } else {
                    var ui_next_send = ui_idx + 1;

                    loop_send(ui_next_send, ui_total);
                }
            } else {
                var arr_send_req_data = mc_get_send_card_req_img_array(ui_total);

                // 请求发送卡图像信息
                mc_get_hw_send_card_img_rect("get", arr_send_req_data, function (b_err) {
                    if (!b_err) {
                        for (var ui_i = 0; ui_i < ui_total; ui_i++) {
                            var item = arr_send_req_data[ui_i];

                            if (!item) {
                                continue;
                            }

                            var ui_send_i = Number(item.INDEX);
                            var obj_send_data = g_obj_instance_card_data.new_child_send("get", ui_send_i);

                            g_obj_instance_card_data.set_img(obj_send_data, item.VALUE);
                        }
                    }

                    callback();
                    return true;
                });
            }
        }


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    获取发送卡输出口数据
         * 参数:
         *    @param { Promise<Number> } ui_child_num 发送卡子集个数 === 输出口总数
         *    @param { Promise<Object> } obj_send_idx 发送卡数据  == mc_topo_foramt_card_idx_data 实例
         * 返回:
         *    NA
         * 例子:
         *    NA
         * 备注:
         *    NA
         * 修改:
         *    1. 类型 : 创建
         *       作者 : 巫昭雯
         *       时间 : 2021-01-13
         *       内容 : 所有代码
        ************************************************************************************************/
        function get_send_card_port_child(ui_child_num, obj_send_idx) {
            // 发送卡输出口数量
            var ui_send_port_num = ui_child_num;
            var ui_send_i = obj_send_idx.idx;
            // send card port img rect
            var arr_send_port_img = [];

            // get send port img rect
            mc_sendcard_outport_img_rect(String(ui_send_i), callback_send_port_img_rect);

            // 回调函数;获取发送卡输出口图像信息  res_send_port === 输出口信息
            function callback_send_port_img_rect(res_send_port) {
                // 发送卡输出口
                arr_send_port_img = mc_set_ary_img_data(res_send_port, ui_send_port_num);
                loop_send_port(0, ui_send_port_num);
            }


            // 获取输出口; ui_idx === 输出口下标； ui_total === 总数
            function loop_send_port(ui_idx, ui_total) {
                // 请求字串
                var str_request_param = ui_send_i + ":" + ui_idx;

                if (ui_idx < ui_total) {
                    // 构造端口数据
                    g_obj_instance_card_data.new_child_send_port("set", ui_send_i, ui_idx);

                    // send port img
                    var obj_send_port_data = g_obj_instance_card_data.new_child_send_port("get", ui_send_i, ui_idx);

                    g_obj_instance_card_data.set_img(obj_send_port_data, arr_send_port_img[ui_idx]);

                    // 获取hub卡
                    mc_get_hubcard_connect("(" + str_request_param + ")", callback_hub_card_connect);
                } else {
                    var ui_next_send_card = ui_send_i + 1;

                    loop_send(ui_next_send_card, ui_send_card_total_num);
                }


                /************************************************************************************************
                 * 类型:
                 *    回调参数
                 * 功能:
                 *    获取hub卡连接;
                 * 参数:
                 *    @param { Promise<Boolean> } b_success true === 成功 || false === 失败(提示lang_id)
                 *    @param { Promise<String> } str_hub_num HUB数量
                 *    @param { Promise<Array> } arr_hubport_num HUBPORT数量数组(下标为HUB索引)
                 * 返回:
                *    NA
                 * 例子:
                 *    NA
                 * 备注:
                 *    NA
                 * 修改:
                 *    1. 类型 : 创建
                 *       作者 : 巫昭雯
                 *       时间 : 2021-01-20
                 *       内容 : 所有代码
                ************************************************************************************************/
                function callback_hub_card_connect(b_success, str_hub_num, arr_hubport_num) {
                    if (b_success) {
                        var obj_send_i_data = new mc_topo_foramt_card_idx_data(ui_send_i, ui_idx);

                        loop_hub(0, Number(str_hub_num), obj_send_i_data, arr_hubport_num);
                    } else {
                        // 获取发送卡下的接收卡; 返回接收卡数据对象
                        mc_get_recvcard_connect("(" + str_request_param + ":0:0)", callback_send_port_recive);
                    }
                }


                /************************************************************************************************
                 * 类型:
                 *    回调函数
                 * 功能:
                 *    获取发送卡端口下的接收卡连接
                 * 参数:
                 *    @param { Promise<String> } str_recvcard_num 接收卡数量
                 * 返回:
                 *    NA
                 * 例子:
                 *    NA
                 * 备注:
                 *    NA
                 * 修改:
                 *    1. 类型 : 创建
                 *       作者 : 巫昭雯
                 *       时间 : 2021-01-20
                 *       内容 : 所有代码
                ************************************************************************************************/
                function callback_send_port_recive(str_recvcard_num) {
                    if ("string" === typeof str_recvcard_num && 0 !== str_recvcard_num.length) {
                        var obj_recvcard_type = mc_get_recvcard_msg(str_recvcard_num);

                        if ("object" === typeof obj_recvcard_type) {
                            // add receive card
                            var arr_send_idx = [ui_send_i, ui_idx];

                            g_obj_instance_card_data.new_child_receive("set", "SEND", arr_send_idx, true, Number(obj_recvcard_type.recvcard_num));

                            // // 测试数据
                            // var ui_ran = Number((Math.random() * 10).toFixed());

                            // g_obj_instance_card_data.new_child_receive("set", "SEND", arr_send_idx, true, ui_ran);
                        }
                    }

                    // 下一个发送卡输出口
                    var ui_next_send_port_i = ui_idx + 1;

                    loop_send_port(ui_next_send_port_i, ui_send_port_num);
                }
            }


            /************************************************************************************************
             * 类型:
             *    函数
             * 功能:
             *    获取hub卡下接收卡
             * 参数:
             *    @param { Promise<String> } ui_idx hub下标
             *    @param { Promise<String> } ui_total hub总数
             *    @param { Promise<Object> } obj_send_data 发送卡数据  == mc_topo_foramt_card_idx_data 实例
             *    @param { Promise<String> } arr_port_num hub输出口数量(数组); 数组下标 === 输出口下标
             * 返回:
             *    NA
             * 例子:
             *    NA
             * 备注:
             *    NA
             * 修改:
             *    1. 类型 : 创建
             *       作者 : 巫昭雯
             *       时间 : 2021-01-13
             *       内容 : 所有代码
            ************************************************************************************************/
            function loop_hub(ui_idx, ui_total, obj_send_data, arr_port_num) {
                // get send card index data
                var ui_send_idx = obj_send_data.idx;
                var ui_send_port_idx = obj_send_data.port_idx;

                if (ui_idx < ui_total) {
                    g_obj_instance_card_data.new_child_hub("set", ui_send_idx, ui_send_port_idx, ui_idx);

                    // 单张hub卡的端口数量
                    var ui_port_len = arr_port_num[ui_idx];

                    // 构造hub卡端口
                    for (var ui_i_port = 0; ui_i_port < ui_port_len; ui_i_port++) {
                        g_obj_instance_card_data.new_child_hub_port("set", ui_send_idx, ui_send_port_idx, ui_idx, ui_i_port);
                    }

                    // hub port img
                    var str_req_img = ui_send_idx + ":" + ui_send_port_idx + ":" + ui_idx;

                    mc_get_hubcard_outport_img_rect(str_req_img, callback_hub_port_img_rect);

                    var obj_hub_idx_data = new mc_topo_foramt_card_idx_data(ui_idx, null);

                    // 读取hub卡多个端口下的接收卡
                    mc_get_multi_recvcard_connect(obj_send_data, obj_hub_idx_data, arr_port_num[ui_idx], "", callback_hub_port_recive);
                } else {
                    // 下一个发送卡输出口
                    var ui_next_send_port_i = ui_send_port_idx + 1;

                    loop_send_port(ui_next_send_port_i, ui_send_port_num);
                    return;
                }


                // 回调函数; 获取hub卡输出口图像; res_hub_port_img === 输出口图像字串(多个数据用"," 分割)
                function callback_hub_port_img_rect(res_hub_port_img) {
                    if (res_hub_port_img) {
                        var ui_hub_port_len = arr_port_num[ui_idx];
                        var arr_hub_port_img = res_hub_port_img.split(",");

                        for (var ui_port_i = 0; ui_port_i < ui_hub_port_len; ui_port_i++) {
                            var str_img = arr_hub_port_img[ui_port_i];
                            var obj_hub_port_data = g_obj_instance_card_data.new_child_hub_port("get", ui_send_idx, ui_send_port_idx, ui_idx, ui_port_i);

                            g_obj_instance_card_data.set_img(obj_hub_port_data, str_img);
                        }
                    }
                }

                // 回调函数； 获取hub输出口下的接收卡; res === 数组(多个输出口)
                function callback_hub_port_recive(res) {
                    if (res) {
                        var arr_res = res;
                        var ui_len = arr_port_num[ui_idx];

                        // ui_i_res = hub输出口下标
                        for (var ui_i_res = 0; ui_i_res < ui_len; ui_i_res++) {
                            var obj_item = arr_res[ui_i_res];
                            var res_value_i = obj_item.value;
                            var arr_idx = [ui_send_idx, ui_send_port_idx, ui_idx, ui_i_res];

                            if ("object" === typeof res_value_i) {
                                // add receive card
                                g_obj_instance_card_data.new_child_receive("set", "HUB", arr_idx, true, res_value_i.recvcard_num);
                            }

                            // // 测试数据
                            // var ui_ran = Number((Math.random() * 10).toFixed());

                            // g_obj_instance_card_data.new_child_receive("set", "HUB", arr_idx, true, ui_ran);
                        }
                    }

                    // 读取下一张hub卡
                    var ui_next_hub_i = ui_idx + 1;

                    loop_hub(ui_next_hub_i, ui_total, obj_send_data, arr_port_num);
                }
            }
        }


        return true;
    }


    return true;
}

/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    切换布局
 * 参数:
 *    @param { Promise<String> } b_ver 是否为横向; true === 横向
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2021-01-22
 *       内容 : 所有代码
************************************************************************************************/
function mc_hw_swich_layout(b_ver) {
    var $obj_container = $("#" + g_id_canvas_topo);
    var str_class = "layout_vertical";
    var str_opt = "removeClass";

    if (b_ver) {
        str_opt = "addClass";
    }

    $obj_container[str_opt](str_class);
    mc_hw_topo_connect_line(g_obj_instance_card_data.get_data());
    init_svg_size();
}
